﻿#include "CSocketListener.h"
#include "assert.h"

CSocketListener::CSocketListener() :m_bInit(false), m_bStart(false), m_iSocketErr(0)
{}

CSocketListener::~CSocketListener()
{
	CloseListener();
}

int CSocketListener::InitListener(ST_SERVERINFO_OBJ serverInfo)
{
	assert(serverInfo.port > 0);
	assert(strlen(serverInfo.ip) > 0);
	if (m_bStart)
		return INITERR_STARTING;
	if (m_bInit)
		return INITERR_INITED;
	int iInitSocket = InitListenSocket(serverInfo);
	m_bInit = iInitSocket == 0;
	return iInitSocket;
}

int CSocketListener::GetSocketError()
{
	return m_iSocketErr;
}

int CSocketListener::InitListenSocket(ST_SERVERINFO_OBJ serverInfo)
{
	WSADATA wsaData;
	int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (err != 0)
	{
		return INITERR_WSASTARTUPERR;
	}
	if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
	{
		WSACleanup();
		return INITERR_WSAVERSIONERR;
	}
	m_sckServerSocket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (m_sckServerSocket == INVALID_SOCKET) {
		m_iSocketErr = WSAGetLastError();
		return INITERR_CREATESOCKETERR;
	}

	int error = 0;
	sockaddr_in addr_in;
	addr_in.sin_family = AF_INET;
	addr_in.sin_port = htons(serverInfo.port);
	addr_in.sin_addr.s_addr = INADDR_ANY;
	error = ::bind(m_sckServerSocket, (sockaddr*)&addr_in, sizeof(sockaddr_in));
	if (error == SOCKET_ERROR) {
		m_iSocketErr = WSAGetLastError();
		::closesocket(m_sckServerSocket);
		return INITERR_BINDSOCKETERR;
	}

	listen(m_sckServerSocket, 5);
	if (error == SOCKET_ERROR) {
		m_iSocketErr = WSAGetLastError();
		::closesocket(m_sckServerSocket);
		return INITERR_SOCKETLISTENERR;
	}
	m_hServerEvent = ::WSACreateEvent();
	::WSAEventSelect(m_sckServerSocket, m_hServerEvent, FD_ACCEPT | FD_CLOSE);
	m_hQuitEvent = ::WSACreateEvent();
	return 0;
}

unsigned __stdcall _ThreadProcStartListener(void* arg) {
	THREAD_SOCKETLISTEN_PARAM* pSocketListenParam = (THREAD_SOCKETLISTEN_PARAM*)arg;
	CSocketListener* pSocketListener = pSocketListenParam->pSocketListener;
	CSocketListenAccepter* pSocketListenAccepter = pSocketListenParam->pSocketListenAccepter;
	SOCKET listenSocket = pSocketListener->m_sckServerSocket;
	HANDLE listenEvent = pSocketListener->m_hServerEvent;
	HANDLE quitEvent = pSocketListener->m_hQuitEvent;

	int nEvent = 0;
	int iQuitEvent = 0;
	WSAEVENT eventArray[2];

	eventArray[iQuitEvent = nEvent++] = quitEvent;
	eventArray[nEvent++] = listenEvent;
	while (TRUE)
	{
		int nIndex = ::WSAWaitForMultipleEvents(nEvent, eventArray, FALSE, WSA_INFINITE, FALSE);
		if (nIndex == WSA_WAIT_FAILED)
		{
			//失败退出监听
			break;
		}
		else if (nIndex == WSA_WAIT_TIMEOUT)
		{
			continue;
		}
		else if (nIndex == iQuitEvent) {
			//主线程通知退出
			::WSAResetEvent(eventArray[nIndex]);
			break;
		}
		else
		{
			nIndex = nIndex - WSA_WAIT_EVENT_0;
			WSANETWORKEVENTS wsaNetEvent;
			if (::WSAEnumNetworkEvents(listenSocket, listenEvent, &wsaNetEvent) == SOCKET_ERROR)
			{
				int iErrRet = WSAGetLastError();
				switch (iErrRet) 
				{
				case WSANOTINITIALISED:outputStr("listen error:WSANOTINITIALISED",strlen("listen error:WSANOTINITIALISED")); break;
				case WSAENETDOWN:outputStr("listen error:WSAENETDOWN",strlen("listen error:WSAENETDOWN")); break;
				case WSAEINVAL:outputStr("listen error:WSAEINVAL",strlen("listen error:WSAEINVAL")); break;
				case WSAEINPROGRESS:outputStr("listen error:WSAEINPROGRESS",strlen("listen error:WSAEINPROGRESS")); break;
				default:break;
				}
				break;
			}
			if (wsaNetEvent.lNetworkEvents & FD_ACCEPT)
			{
				if (wsaNetEvent.iErrorCode[FD_ACCEPT_BIT] == 0)
				{
					while (TRUE)
					{
						//把等待连接队列中的用户都接受建立请求，建立后立即派送给相关的服务线程处理
						SOCKADDR_IN addrClient;
						int iLen = sizeof(SOCKADDR);
						SOCKET s = accept(listenSocket, (SOCKADDR*)&addrClient, &iLen);
						if (s == SOCKET_ERROR)
						{
							//没有连接请求了
							break;
						}
						PSOCKETOBJ pSock = ST_SOCKET_OBJ::GetSocketObj(s);
						if (pSock == NULL)
						{
							::closesocket(s);
							continue;
						}
						memcpy_s(&pSock->addrClient, sizeof(SOCKADDR_IN), &addrClient, \
							sizeof(SOCKADDR_IN));
						//设置客户端socket关心的事件
						::WSAEventSelect(pSock->s, pSock->hEvent, FD_WRITE | FD_READ | FD_CLOSE);
						if (!pSocketListenAccepter->Accept(pSock))
						{
							delete pSock;
						}
					}
				}
			}
			if (wsaNetEvent.lNetworkEvents & FD_CLOSE)
			{
				break;
			}
		}
	}
	delete pSocketListenParam;
	return 0;
}
int CSocketListener::StartListener(CSocketListenAccepter *pSocketListenAccepter)
{
	if (!m_bInit)
	{
		return STARTLISTENERR_NOINIT;
	}
	if (m_bStart)
	{
		return STARTLISTENERR_STARTED;
	}
	//begin thread
	THREAD_SOCKETLISTEN_PARAM* pSocketListenParam = new THREAD_SOCKETLISTEN_PARAM;
	pSocketListenParam->pSocketListener = this;
	pSocketListenParam->pSocketListenAccepter = pSocketListenAccepter;
	m_pServerThread = (HANDLE)_beginthreadex(NULL, 0, _ThreadProcStartListener, (void*)pSocketListenParam, 0, &m_IServerThreadId);
	m_bStart = true;
	return 0;
}
void CSocketListener::CloseListener()
{
	if (!m_bInit)return;
	//end thread
	if (m_bStart)
	{
		::WSASetEvent(m_hQuitEvent);
		::WaitForSingleObject(m_pServerThread, INFINITE);
		::CloseHandle(m_pServerThread);
	}
	::WSACloseEvent(m_hQuitEvent);
	::WSACloseEvent(m_hServerEvent);
	::closesocket(m_sckServerSocket);
	::WSACleanup();
	m_bStart = m_bInit = false;
}